package com.ikingtech.platform.service.system.menu.service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ikingtech.framework.sdk.base.model.PageResult;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.menu.model.MenuQueryParamDTO;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.platform.service.system.menu.entity.MenuDO;
import com.ikingtech.platform.service.system.menu.exception.MenuExceptionInfo;
import com.ikingtech.platform.service.system.menu.mapper.MenuMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author tie yan
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MenuService extends ServiceImpl<MenuMapper, MenuDO> {

    public String parseFullPath(String parentId, String id) {
        if (Tools.Str.isNotBlank(parentId)) {
            MenuDO parentMenuEntity = this.getById(parentId);
            if (null == parentMenuEntity) {
                throw new FrameworkException(MenuExceptionInfo.PARENT_MENU_NOT_FOUND);
            }
            return parentMenuEntity.getFullPath() + "@" + id;
        } else {
            return id;
        }
    }

    public Map<String, Integer> calculateMenuCount(List<MenuDO> menuEntities) {
        if (Tools.Coll.isBlank(menuEntities)) {
            return new HashMap<>(0);
        }
        Map<String, Integer> menuCountMap = new HashMap<>(menuEntities.size());
        // 遍历每一个菜单节点
        for (MenuDO menuEntity : menuEntities) {
            // 获取当前节点的全路径，按@分割，结果为当前节点编号以及所有父节点编号
            String[] menuIds = menuEntity.getFullPath().split("@");
            // 遍历每一个节点编号
            for (String menuId : menuIds) {
                // 当前节点数量为1，当前节点所有父节点下的节点数量+1
                menuCountMap.put(menuId, menuCountMap.getOrDefault(menuId, 0) + 1);
            }
        }
        return menuCountMap;
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateChildMenuVisibleOrViewType(String fullPath, Boolean visible, String viewType) {
        List<MenuDO> entities = this.list(Wrappers.<MenuDO>lambdaQuery()
                .likeRight(MenuDO::getFullPath, fullPath));
        this.updateBatchById(Tools.Coll.traverse(entities, entity -> {
            entity.setVisible(visible);
            entity.setMenuViewType(viewType);
            return entity;
        }));
    }

    public PageResult<MenuDO> listPage(MenuQueryParamDTO queryParam) {
        return PageResult.build(this.page(new Page<>(queryParam.getPage(), queryParam.getRows()), Wrappers.<MenuDO>lambdaQuery()
                .eq(Tools.Str.isNotBlank(queryParam.getDomainCode()), MenuDO::getDomainCode, queryParam.getDomainCode())
                .eq(Tools.Str.isNotBlank(queryParam.getTenantCode()), MenuDO::getTenantCode, queryParam.getTenantCode())
                .eq(Tools.Str.isNotBlank(queryParam.getAppCode()), MenuDO::getAppCode, queryParam.getAppCode())
                .eq(Tools.Str.isNotBlank(queryParam.getMenuViewType()), MenuDO::getMenuViewType, queryParam.getMenuViewType())
                .eq(Tools.Str.isNotBlank(queryParam.getMenuType()), MenuDO::getMenuType, queryParam.getMenuType())
                .eq(Tools.Str.isNotBlank(queryParam.getMenuJumpType()), MenuDO::getJumpType, queryParam.getMenuJumpType())
                .like(Tools.Str.isNotBlank(queryParam.getName()), MenuDO::getName, queryParam.getName())
                .eq(null != queryParam.getKeepAlive(), MenuDO::getKeepAlive, queryParam.getKeepAlive())
                .like(Tools.Str.isNotBlank(queryParam.getLink()), MenuDO::getLink, queryParam.getLink())
                .eq(null != queryParam.getVisible(), MenuDO::getVisible, queryParam.getVisible())
                .orderByDesc(MenuDO::getCreateTime)));
    }

    public Integer getMaxSortOrder(String parentId, String appCode) {
        MenuDO entity = this.getOne(Wrappers.<MenuDO>lambdaQuery()
                .select(MenuDO::getSortOrder)
                .eq(MenuDO::getParentId, Tools.Str.isBlank(parentId) ? Tools.Str.EMPTY : parentId)
                .eq(Tools.Str.isNotBlank(Me.domainCode()), MenuDO::getDomainCode, Me.domainCode())
                .eq(Tools.Str.isNotBlank(Me.tenantCode()), MenuDO::getTenantCode, Me.tenantCode())
                .eq(Tools.Str.isNotBlank(appCode), MenuDO::getAppCode, appCode)

                .orderByDesc(MenuDO::getSortOrder));
        return null == entity ? 0 : entity.getSortOrder();
    }

    public List<MenuDO> between(String parentId, Integer startValue, Integer endValue) {
        return this.list(Wrappers.<MenuDO>query().lambda()
                .between(MenuDO::getSortOrder, startValue, endValue)
                .eq(MenuDO::getParentId, parentId));
    }

    public List<MenuDO> gt(String parentId, Integer value) {
        return this.list(Wrappers.<MenuDO>query().lambda()
                .eq(MenuDO::getParentId, parentId)
                .gt(MenuDO::getSortOrder, value));
    }

    public List<String> listIdByFullPath(String fullPath) {
        return this.listObjs(Wrappers.<MenuDO>lambdaQuery()
                .select(MenuDO::getId)
                .likeRight(MenuDO::getFullPath, fullPath), String.class::cast);
    }

    public String getIdByPermissionCode(String permissionCode, String tenantCode) {
        return this.getObj(Wrappers.<MenuDO>lambdaQuery()
                .select(MenuDO::getId)
                .eq(MenuDO::getPermissionCode, permissionCode)
                .eq(Tools.Str.isNotBlank(tenantCode), MenuDO::getTenantCode, tenantCode), String.class::cast);
    }

    public boolean existByDomain(String domain) {
        return this.baseMapper.exists(Wrappers.<MenuDO>lambdaQuery()
                .eq(MenuDO::getDomainCode, domain));
    }

    public List<MenuDO> listByDomainCodeAndTenantCodeAndAppCode(String domainCode, String tenantCode, String appCode) {
        return this.list(Wrappers.<MenuDO>lambdaQuery()
                .eq(Tools.Str.isNotBlank(domainCode), MenuDO::getDomainCode, domainCode)
                .eq(Tools.Str.isNotBlank(tenantCode), MenuDO::getTenantCode, tenantCode)
                .eq(Tools.Str.isNotBlank(appCode), MenuDO::getAppCode, appCode));
    }

    public List<String> listIdByDomainCodeAndTenantCodeAndAppCode(String domainCode, String tenantCode, String appCode) {
        return this.listObjs(Wrappers.<MenuDO>lambdaQuery()
                .select(MenuDO::getId)
                .eq(MenuDO::getDomainCode, domainCode)
                .eq(Tools.Str.isNotBlank(tenantCode), MenuDO::getTenantCode, tenantCode)
                .eq(MenuDO::getAppCode, appCode), String.class::cast);
    }

    public List<String> listIdByTenantCode(String tenantCode) {
        return this.listObjs(Wrappers.<MenuDO>lambdaQuery()
                .eq(MenuDO::getTenantCode, tenantCode), String.class::cast);
    }

    public MenuDO getByPermissionCode(String permissionCode, String tenantCode) {
        return this.getOne(Wrappers.<MenuDO>lambdaQuery()
                .eq(MenuDO::getPermissionCode, permissionCode)
                .eq(Tools.Str.isNotBlank(tenantCode), MenuDO::getTenantCode, tenantCode));
    }

    public void removeByPermissionCode(String permissionCode, String tenantCode, String appCode) {
        this.remove(Wrappers.<MenuDO>lambdaQuery()
                .eq(MenuDO::getPermissionCode, permissionCode)
                .eq(Tools.Str.isNotBlank(tenantCode), MenuDO::getTenantCode, tenantCode)
                .eq(Tools.Str.isNotBlank(appCode), MenuDO::getAppCode, appCode));
    }
}
